<template>
  <view class="tn-form">
    <slot></slot>
  </view>
</template>

<script lang="ts" setup>
import { 
  formProps,
  formContextKey, 
  FormItemContext, 
  FormContext, 
  FormValidationResult, 
  FormValidateCallback
} from '../../../types/tn-form'
import { computed, provide, reactive, toRefs } from 'vue'
import { filterFields } from '../lib/utils'
import { Arrayable } from '../../../utils'
// import { ValidateFieldsError } from 'async-validator'
import { ValidateFieldsError } from '../../../utils/async-validate'
import { isFunction } from '@vue/shared'

const props = defineProps(formProps)

// 保存formItem信息
let fields: FormItemContext[] = []

// 保存formItem
const addField: FormContext['addField'] = (field: FormItemContext) => {
  // 如果存在则不进行添加(通过prop来进行判断)
  const index = fields.findIndex((item: FormItemContext) => {
    return item.prop === field.prop
  })
  if (index === -1) fields.push(field)
}
// 移除formItem
const removeField: FormContext['removeField'] = (field: FormItemContext) => {
  // 查找对应的formItem是否存在
  const index = fields.findIndex((item: FormItemContext) => {
    return item.prop === field.prop
  })
  if (index !== -1) fields.splice(index, 1)
}

// 重置formItem信息, 将其值重置为初始值并移除校验结果
const resetFields: FormContext['resetFields'] = (properties = []) => {
  if (!props.model) {
    console.error('[tn-form]modal参数不存在, 重置失败')
    return
  }
  filterFields(fields, properties).forEach((field: FormItemContext) => field.resetField())
}

// 移除校验规则结果
const clearValidate: FormContext['clearValidate'] = (props = []) => {
  filterFields(fields, props).forEach((field: FormItemContext) => field.clearValidate())
}

// 是否进行校验
const isValidateable = computed<boolean>(() => {
  const hasModel = !!props?.model && Object.keys(props.model).length > 0
  if (!hasModel) {
    console.error('[tn-form]model参数不存在, 检验失败')
  }
  return hasModel
})

// 获取进行校验的字段(FormItem)
const obtainValidateFields = (props: Arrayable<Arrayable<string>>): FormItemContext[] | [] => {
  if (fields.length === 0) return []

  const filteredFileds = filterFields(fields, props)
  if (!filteredFileds.length) {
    console.error('[tn-form]prop参数不正确')
    return []
  }
  return filteredFileds
}

// 执行规则校验操作
const doValidateField = async (props: Arrayable<Arrayable<string>> = []): FormValidationResult => {
  // 没有设置model直接验证失败
  if(!isValidateable.value) return false

  // 如果需要进行校验的fields长度为0则直接通过验证
  const fields = obtainValidateFields(props)
  if (fields.length === 0) return true

  // 校验错误规则
  let validationErrors: ValidateFieldsError = {}
  for (const field of fields) {
    try {
      await field.validate('')
    } catch (e) {
      // 新增校验错误的规则
      validationErrors = {
        ...validationErrors,
        ...(e as ValidateFieldsError)
      }
    }
  }

  // 返回校验结果
  if (Object.keys(validationErrors).length === 0) return true
  return Promise.reject(validationErrors)
}

// 校验规则
const validateField: FormContext['validateField'] = async (modelProps = [], callback) => {
  // 判断callback是否为function, 如果不是则抛出异常
  const shouldThrow = !isFunction(callback)
  try {
    const result = await doValidateField(modelProps)
    // 如果result为false则校验失败
    if (result === true) {
      callback?.(result)
    }
    return result
  } catch (e) {
    const invalidFields = e as ValidateFieldsError
    callback?.(false, invalidFields)
    return shouldThrow && Promise.reject(invalidFields)
  }
}

const validate = async (callback?: FormValidateCallback): FormValidationResult => validateField(undefined, callback)

provide(formContextKey, reactive({
  ...toRefs(props),

  addField,
  removeField,
  resetFields,
  clearValidate,
  validateField
}))

defineExpose({
  validate,
  resetFields,
  clearValidate
})
</script>

<script lang="ts">
export default {
  options: {
    // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
    virtualHost: true
  }
}
</script>

<style lang="scss" scoped>
</style>
